#include "ServerEndpoint.h"
#include <filesystem>
#include <rapidjson/document.h>

using namespace std;
using namespace Pistache;
using namespace Pistache::Http;
using namespace Pistache::Rest;
using namespace rapidjson;

ServerEndpoint::ServerEndpoint(const IP &ip, const Port &port, size_t thr)
{
    address = Address(ip, port);
    httpEndpoint = make_shared<Endpoint>(address);
    elasticSearchClient = make_shared<ElasticsearchClient>("192.168.56.51", 9200);
    hbaseClient = make_shared<HBaseClient>("192.168.56.51", 8080);
    hdfsClient = make_shared<HdfsClient>("192.168.56.51", 14000);
    auto opts = Http::Endpoint::options().threads(static_cast<int>(thr)).maxRequestSize(128 * 1024 * 1024);
    httpEndpoint->init(opts);
}

ServerEndpoint::~ServerEndpoint()
{
    httpEndpoint->shutdown();
}

void ServerEndpoint::Start()
{
    SetupRoutes();
    httpEndpoint->setHandler(router.handler());
    httpEndpoint->serve();
}

void ServerEndpoint::AsyncStart()
{
    SetupRoutes();
    httpEndpoint->setHandler(router.handler());
    httpEndpoint->serveThreaded();
}

void ServerEndpoint::Stop()
{
    httpEndpoint->shutdown();
}

void ServerEndpoint::AddAnalyzeHandle(const string &name, const string &desc, HandleFunc func)
{
    handles.insert(make_pair(name, tie(desc,func)));
}

void ServerEndpoint::SetupRoutes()
{
    Routes::Get(router, "/say_hello", Routes::bind(&ServerEndpoint::SayHello, this));

    Routes::Get(router, "/manage/getStatus", Routes::bind(&ServerEndpoint::ManageStatus, this));
    Routes::Put(router, "/manage/:tableName", Routes::bind(&ServerEndpoint::ManagePut, this));
    Routes::Get(router, "/manage/:tableName/:rowKey/:column?", Routes::bind(&ServerEndpoint::ManageGet, this));
    Routes::Delete(router, "/manage/:tableName/:rowKey?/:column?", Routes::bind(&ServerEndpoint::ManageDel, this));

    Routes::Put(router, "/file/:url", Routes::bind(&ServerEndpoint::FilePut, this));
    Routes::Get(router, "/file/:url", Routes::bind(&ServerEndpoint::FileGet, this));
    Routes::Delete(router, "/file/:url", Routes::bind(&ServerEndpoint::FileDel, this));

    Routes::Get(router, "/search/:tableName", Routes::bind(&ServerEndpoint::Search, this));

    Routes::Get(router, "/analyze/manage", Routes::bind(&ServerEndpoint::AnalyzeManage, this));

    for (auto& i : handles)
        Routes::Get(router, string("/analyze/") + i.first , Routes::bind(get<1>(i.second)));

    Routes::NotFound(router, Routes::bind(&ServerEndpoint::DefaultHandle, this));
}


void ServerEndpoint::DefaultHandle(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
#define CLION_BUILD
#ifdef CLION_BUILD
    const auto &basePath = Application::ApplicationPath() + "/../../serverFile";
#else
    const auto& basePath = Application::ApplicationPath() + "/../serverFile";
#endif
    const auto &resource = request.resource();
    auto filePath = basePath + resource;
    if (filesystem::is_directory(filePath))
    {
        if (!StrEndsWith(filePath, '/'))
            filePath += '/';
        filePath += "index.html";
    }
    if (filesystem::exists(filePath))
    {
        auto mime = MIME(Application, OctetStream);
        do
        {
            if (StrEndsWith(filePath, ".js"))
            {
                mime = MIME(Text, Javascript);
                break;
            }
            if (StrEndsWith(filePath, ".html") || StrEndsWith(filePath, ".htm"))
            {
                mime = MIME(Text, Html);
                break;
            }
            if (StrEndsWith(filePath, ".css"))
            {
                mime = MIME(Text, Css);
                break;
            }
            if (StrEndsWith(filePath, ".svg"))
            {
                mime = MIME(Image, Svg);
                break;
            }
            if (StrEndsWith(filePath, ".png"))
            {
                mime = MIME(Image, Png);
                break;
            }
            if (StrEndsWith(filePath, ".jpeg") || StrEndsWith(filePath, ".jpg"))
            {
                mime = MIME(Image, Jpeg);
                break;
            }
            if (StrEndsWith(filePath, ".gif"))
            {
                mime = MIME(Image, Gif);
                break;
            }
            if (StrEndsWith(filePath, ".ico"))
            {
                mime = MIME(Image, Ico);
                break;
            }
        } while (false);
        serveFile(response, filePath, mime);
        return;
    } else if (filesystem::exists(basePath + "/404.html"))
    {
        ifstream file;
        file.open(basePath + "/404.html", ios::binary | ios::ate);
        string body;
        if (file.is_open())
        {
            int length = file.tellg();
            char chars[length];
            file.seekg(0, ios::beg);
            file.read(chars, length);
            file.close();
            body.append(chars, length);
            response.send(Code::Not_Found, body, MIME(Text, Html));
            return;
        } else
        {
            response.send(Code::Not_Found, "Service not found.");
            return;
        }
    } else
    {
        response.send(Code::Not_Found, "Service not found.");
        return;
    }
}

void ServerEndpoint::SayHello(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    response.send(Code::Ok, "Hello World!");
}

void ServerEndpoint::ManageStatus(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto res = Document();
    res.SetObject();
    res.AddMember("Hdfs", Document().SetBool(hdfsClient->Ok()), res.GetAllocator());
    res.AddMember("HBase", Document().SetBool(hbaseClient->Ok()), res.GetAllocator());
    res.AddMember("Es", Document().SetBool(elasticSearchClient->Ok()), res.GetAllocator());
    response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
}

void ServerEndpoint::ManagePut(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto rowsShell = RowsShell();
    auto esCells = ESCells();
    auto tableName = string();
    auto dom = Document();
    auto res = Document();
    res.SetObject();
    dom.Parse(request.body().c_str());
    auto warning = Document();
    warning.SetArray();
    res.AddMember("Warning", warning, res.GetAllocator());
    if (!dom.HasMember("tableName") || !dom["tableName"].IsString())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json string member "tableName" is required.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    if (!dom.HasMember("rows") || !dom["rows"].IsArray())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json array member "rows" is required.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    tableName = dom["tableName"].GetString();
    if (tableName.empty())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json string member "tableName" is empty.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    esCells.IndexName = tableName;
    auto rowsReceived = dom["rows"].GetArray();
    for (auto &row : rowsReceived)
    {
        if (!row.HasMember("rowKey") || !row["rowKey"].IsString())
        {
            res["Warning"].GetArray().PushBack(Document().SetString(
                    R"(Warning: Json string member "rowKey" in Object in json array "rows" can not be found, skip.)",
                    res.GetAllocator()), res.GetAllocator());
            continue;
        }
        if (!row.HasMember("columns") || !row["columns"].IsArray())
        {
            res["Warning"].GetArray().PushBack(Document().SetString(
                    R"(Warning: Json array member "columns" in Object in json array "rows" can not be found, skip.)",
                    res.GetAllocator()), res.GetAllocator());
            continue;
        }
        auto hbaseRow = Row();
        auto esRow = ESRow();
        hbaseRow.Key = row["rowKey"].GetString();
        if (hbaseRow.Key.empty())
        {
            res["Warning"].GetArray().PushBack(Document().SetString(
                    R"(Warning: Json string member "rowKey" in Object in json array "rows" is empty, skip.)",
                    res.GetAllocator()), res.GetAllocator());
            continue;
        }
        esRow.Key = hbaseRow.Key;
        auto columnReceived = row["columns"].GetArray();
        for (auto &column : columnReceived)
        {
            if (!column.HasMember("column") || !column["column"].IsString())
            {
                res["Warning"].GetArray().PushBack(Document().SetString(
                        R"(Warning: Json string member "column" in Object in json array "columns" can not be found, skip.)",
                        res.GetAllocator()), res.GetAllocator());
                continue;
            }
            if (!column.HasMember("value") || !column["value"].IsString())
            {
                res["Warning"].GetArray().PushBack(Document().SetString(
                        R"(Warning: Json string member "value" in Object in json array "columns" can not be found, skip.)",
                        res.GetAllocator()), res.GetAllocator());
                continue;
            }
            string columnName = column["column"].GetString();
            if (columnName.empty())
            {
                res["Warning"].GetArray().PushBack(Document().SetString(
                        R"(Warning: Json string member "column" in Object in json array "columns" is empty, skip.)",
                        res.GetAllocator()), res.GetAllocator());
                continue;
            }
            string value = column["value"].GetString();
            if (value.empty())
            {
                res["Warning"].GetArray().PushBack(Document().SetString(
                        R"(Warning: Json string member "value" in Object in json array "columns" is empty, skip.)",
                        res.GetAllocator()), res.GetAllocator());
                continue;
            }
            hbaseRow.Cells.emplace_back(Cell(string("_doc:") + columnName, value));
            bool canSearch;
            if (!column.HasMember("canSearch") || !column["canSearch"].IsBool())
                canSearch = false;
            else
                canSearch = column["canSearch"].GetBool();
            if (canSearch)
            {
                bool canCut;
                if (!column.HasMember("canCut") || !column["canCut"].IsBool())
                    canCut = false;
                else
                    canCut = column["canCut"].GetBool();
                esRow.Cells.emplace_back(ESCell(columnName, value, canCut));
            }
        }
        if (hbaseRow.Cells.empty())
        {
            stringstream ss;
            ss << R"(Warning: Json array member "columns" in row)" << hbaseRow.Key << "is empty, skip.";
            res["Warning"].GetArray().PushBack(Document().SetString(ss.str().c_str(), res.GetAllocator()),
                                               res.GetAllocator());
            continue;
        }
        rowsShell.Rows.emplace_back(hbaseRow);
        if (!esRow.Cells.empty())
            esCells.Rows.emplace_back(esRow);
    }
    if (rowsShell.Rows.empty())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json array member "rows" is empty.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    bool hbaseResult = hbaseClient->PutCells(tableName, rowsShell);
    if (!hbaseResult)
    {
        res.AddMember("Error", Document().SetString(R"(Error: Can not add data to HBase.)"), res.GetAllocator());
        response.send(Code::Internal_Server_Error, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    if (!esCells.Rows.empty())
    {
        bool esResult = elasticSearchClient->CreateDocument(tableName, esCells);
        if (!esResult)
        {
            res.AddMember("Error", Document().SetString(R"(Error: Can not add data to ElasticSearch.)"),
                          res.GetAllocator());
            response.send(Code::Internal_Server_Error, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
            return;
        }
    }
    response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
}

void ServerEndpoint::ManageGet(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto res = Document();
    res.SetObject();
    res.AddMember("Warning", Document().SetArray(), res.GetAllocator());
    auto warning = res["Warning"].GetArray();
    res.AddMember("Result", Document().SetObject(), res.GetAllocator());
    auto result = res["Result"].GetObject();
    auto tableName = Url::URLDecode(request.param(":tableName").as<string>());
    auto rowKey = Url::URLDecode(request.param(":rowKey").as<string>());
    if (request.hasParam(":column"))
    {
        auto column = Url::URLDecode(request.param(":column").as<string>());
        auto cell = hbaseClient->GetCell(tableName, rowKey, column);
        if (!cell.has_value())
        {
            res.AddMember("Error", Document().SetString("Error: Cell not found."), res.GetAllocator());
            response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
            return;
        }
        stringstream ss;
        ss << R"({"rowKey":")";
        ss << rowKey;
        ss << R"(","cells":[{"column":")";
        ss << column;
        ss << R"(","value":")";
        ss << cell.value().value;
        ss << R"("}]})";
        response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    auto row = hbaseClient->GetRow(tableName, rowKey);
    if (!row.has_value())
    {
        res.AddMember("Error", Document().SetString("Error: Cell not found."), res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    result.AddMember("rowKey", Document().SetString(rowKey.c_str(), rowKey.length(), res.GetAllocator()), res.GetAllocator());
    result.AddMember("cells", Document().SetArray(), res.GetAllocator());
    auto cells = result["cells"].GetArray();
    for (auto& i : row.value().Cells)
    {
        auto cell = Document();
        cell.SetObject();
        auto column = RemoveFirstSubString(i.column, "_doc:");
        cell.AddMember("column", Document().SetString(column.c_str(), column.length(), res.GetAllocator()), res.GetAllocator());
        cell.AddMember("value", Document().SetString(i.value.c_str(), i.value.length(), res.GetAllocator()), res.GetAllocator());
        cells.PushBack(cell, res.GetAllocator());
    }
    response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
}

void ServerEndpoint::ManageDel(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto dom = Document();
    auto res = Document();
    res.SetObject();
    dom.Parse(request.body().c_str());
    auto warning = Document();
    warning.SetArray();
    res.AddMember("Warning", warning, res.GetAllocator());
    auto tableName = Url::URLDecode(request.param(":tableName").as<string>());
    if (request.hasParam(":rowKey"))
    {
        auto rowKey = Url::URLDecode(request.param(":rowKey").as<string>());
        if (!hbaseClient->DeleteRow(tableName, rowKey))
        {
            stringstream ss;
            ss << R"(Warning: Failed to delete row )" << rowKey << " in hbase, skip.";
            res.AddMember("Error", Document().SetString(ss.str().c_str(), res.GetAllocator()), res.GetAllocator());
            response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
            return;
        }
        if (!elasticSearchClient->DeleteDocument(tableName, rowKey))
        {
            stringstream ss;
            ss << R"(Warning: Failed to delete row )" << rowKey << " in elasticsearch, "
               << elasticSearchClient->GetErrorString() << ", skip.";
            res.AddMember("Error", Document().SetString(ss.str().c_str(), res.GetAllocator()), res.GetAllocator());
            response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
            return;
        }
        response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    if (request.hasParam(":column"))
    {
        auto rowKey = Url::URLDecode(request.param(":rowKey").as<string>());
        auto column = Url::URLDecode(request.param(":column").as<string>());
        if (!hbaseClient->DeleteCell(tableName, rowKey, string("_doc:") + column))
        {
            stringstream ss;
            ss << R"(Error: Failed to delete cell at )" << rowKey << " " << column << " in hbase, skip.";
            res.AddMember("Error", Document().SetString(ss.str().c_str(), res.GetAllocator()), res.GetAllocator());
            response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
            return;
        }
        if (!elasticSearchClient->DeleteDocument(tableName, rowKey, column))
        {
            stringstream ss;
            ss << R"(Warning: Failed to delete cell at )" << rowKey << " " << column << " in elasticsearch, "
               << elasticSearchClient->GetErrorString() << ", skip.";
            res.AddMember("Error", Document().SetString(ss.str().c_str(), res.GetAllocator()), res.GetAllocator());
            response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
            return;
        }
        return;
    }
    if (!dom.HasMember("tableName") || !dom["tableName"].IsString())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json string member "tableName" is required.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    if (!dom.HasMember("rows") || !dom["rows"].IsArray())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json array member "rows" is required.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    tableName = dom["tableName"].GetString();
    if (tableName.empty())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json string member "tableName" is empty.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    auto rowsReceived = dom["rows"].GetArray();
    list<tuple<string, string>> rows;
    for (auto &row : rowsReceived)
    {
        if (!row.HasMember("rowKey") || !row["rowKey"].IsString())
        {
            res["Warning"].GetArray().PushBack(Document().SetString(
                    R"(Warning: Json string member "rowKey" in Object in json array "rows" can not be found, skip.)",
                    res.GetAllocator()), res.GetAllocator());
            continue;
        }
        if (!row.HasMember("column") || !row["column"].IsString())
        {
            res["Warning"].GetArray().PushBack(Document().SetString(
                    R"(Warning: Json string member "columns" in Object in json array "rows" can not be found, skip.)",
                    res.GetAllocator()), res.GetAllocator());
            continue;
        }
        string rowKey = row["rowKey"].GetString();
        if (rowKey.empty())
        {
            res["Warning"].GetArray().PushBack(Document().SetString(
                    R"(Warning: Json string member "rowKey" in Object in json array "rows" is empty, skip.)",
                    res.GetAllocator()), res.GetAllocator());
            continue;
        }
        string column = row["column"].GetString();
        if (column.empty())
        {
            res["Warning"].GetArray().PushBack(Document().SetString(
                    R"(Warning: Json string member "column" in Object in json array "rows" is empty, skip.)",
                    res.GetAllocator()), res.GetAllocator());
            continue;
        }
        rows.emplace_back(tie(rowKey, column));
    }
    if (rows.empty())
    {
        res.AddMember("Error", Document().SetString(R"(Error: Json array member "rows" is empty.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    for (auto &i : rows)
    {
        auto rowKey = get<0>(i);
        auto column = get<1>(i);
        if (column == "*")
        {
            if (!hbaseClient->DeleteRow(tableName, rowKey))
            {
                stringstream ss;
                ss << R"(Warning: Failed to delete row )" << rowKey << " in hbase, skip.";
                res["Warning"].GetArray().PushBack(Document().SetString(ss.str().c_str(), res.GetAllocator()),
                                                   res.GetAllocator());
                continue;
            }
            if (!elasticSearchClient->DeleteDocument(tableName, rowKey))
            {
                stringstream ss;
                ss << R"(Warning: Failed to delete row )" << rowKey << " in elasticsearch, "
                   << elasticSearchClient->GetErrorString() << ", skip.";
                res["Warning"].GetArray().PushBack(Document().SetString(ss.str().c_str(), res.GetAllocator()),
                                                   res.GetAllocator());
                continue;
            }
        } else
        {
            if (!hbaseClient->DeleteCell(tableName, rowKey, string("_doc:") + column))
            {
                stringstream ss;
                ss << R"(Warning: Failed to delete cell at )" << rowKey << " " << column << " in hbase, skip.";
                res["Warning"].GetArray().PushBack(Document().SetString(ss.str().c_str(), res.GetAllocator()),
                                                   res.GetAllocator());
                continue;
            }
            if (!elasticSearchClient->DeleteDocument(tableName, rowKey, column))
            {
                stringstream ss;
                ss << R"(Warning: Failed to delete cell at )" << rowKey << " " << column << " in elasticsearch, "
                   << elasticSearchClient->GetErrorString() << ", skip.";
                res["Warning"].GetArray().PushBack(Document().SetString(ss.str().c_str(), res.GetAllocator()),
                                                   res.GetAllocator());
                continue;
            }
        }
    }
    response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
}

void ServerEndpoint::FilePut(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto url = Base64Decode(request.param(":url").as<string>());
    auto result = hdfsClient->UploadContent(url, request.body());
    if (result)
        response.send(Code::Ok);
    else
        response.send(Code::Internal_Server_Error);
}

void ServerEndpoint::FileGet(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto url = Base64Decode(request.param(":url").as<string>());
    auto content = string();
    auto result = hdfsClient->DownloadContent(url, content);
    if (result)
    {
        response.send(Code::Ok, content, MIME(Application, OctetStream));
    }
    if (result)
    {
        response.send(Code::Internal_Server_Error);
    }
}

void ServerEndpoint::FileDel(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto url = Base64Decode(request.param(":url").as<string>());
    auto result = hdfsClient->DeleteFile(url);
    if (result)
        response.send(Code::Ok);
    else
        response.send(Code::Internal_Server_Error);
}

void ServerEndpoint::Search(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto res = Document();
    res.SetObject();
    res.AddMember("Warning", Document().SetArray(), res.GetAllocator());
    auto warning = res["Warning"].GetArray();
    res.AddMember("Result", Document().SetArray(), res.GetAllocator());
    auto result = res["Result"].GetArray();

    auto tableName = Url::URLDecode(request.param(":tableName").as<string>());
    string keyword;
    auto &query = request.query();
    if (query.has("keyword"))
    {
        keyword = Url::URLDecode(query.get("keyword").get());
    }
    else
    {
        res.AddMember("Error", Document().SetString(R"(A query "keyword" is required.)"),
                      res.GetAllocator());
        response.send(Code::Bad_Request, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    uint64_t from = 0, size = 10;
    if (query.has("from"))
    {
        auto text = query.get("from").get();
        if (IsUint(text))
            from = stol(text);
    }
    if (query.has("size"))
    {
        auto text = query.get("size").get();
        if (IsUint(text))
            size = stol(text);
    }
    auto esResult = elasticSearchClient->SearchInIndex(tableName, keyword, from, size);
    if (!esResult.has_value())
    {
        res.AddMember("Error", Document().SetString(R"(Can not get data from Elasticsearch.)"),
                      res.GetAllocator());
        response.send(Code::Internal_Server_Error, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
        return;
    }
    for (auto i : esResult.value())
    {
        auto info = Document();
        info.SetObject();
        auto rowKey = get<0>(i);
        auto column = get<1>(i);
        auto value = get<2>(i);
        info.AddMember("rowKey", Document().SetString(rowKey.c_str(), rowKey.length(), res.GetAllocator()), res.GetAllocator());
        info.AddMember("column", Document().SetString(column.c_str(), column.length(), res.GetAllocator()), res.GetAllocator());
        info.AddMember("value", Document().SetString(value.c_str(), value.length(), res.GetAllocator()), res.GetAllocator());
        result.PushBack(info, res.GetAllocator());
    }
    response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(res)), MIME(Application, Json));
}

void ServerEndpoint::AnalyzeManage(const Rest::Request &request, Pistache::Http::ResponseWriter response)
{
    auto dom = Document();
    dom.SetObject();
    dom.AddMember("available", Document().SetArray(), dom.GetAllocator());
    auto array = dom["available"].GetArray();
    for (auto& i : handles)
    {
        auto content = Document();
        content.SetObject();
        auto name = i.first;
        auto desc = get<0>(i.second);
        content.AddMember("name", Document().SetString(name.c_str(), name.length(), dom.GetAllocator()), dom.GetAllocator());
        content.AddMember("desc", Document().SetString(desc.c_str(), desc.length(), dom.GetAllocator()), dom.GetAllocator());
        array.PushBack(content, dom.GetAllocator());
    }
    response.send(Code::Ok, RemoveRedundancyChars(GetJsonString(dom)), MIME(Application, Json));
}
